#ifndef __M_FMT_H__
#define __M_FMT_H__

#include "message.hpp"
#include <iostream>
#include <memory>
#include <vector>
#include <cassert>
#include <time.h>

using namespace std;

class FormatItem
{
public:
    using ptr = shared_ptr<FormatItem>;
    virtual ~FormatItem() {}
    virtual void format(ostream &out, const LogMsg &msg) = 0;
};

class MsgFormatItem : public FormatItem
{
public:
    virtual void format(ostream &out, const LogMsg &msg)
    {
        out << msg._payload;
    }
};

class LevelFormatItem : public FormatItem
{
public:
    virtual void format(ostream &out, const LogMsg &msg)
    {
        out << LogLevel::toString(msg._level);
    }
};

class NameFormatItem : public FormatItem
{
public:
    virtual void format(ostream &out, const LogMsg &msg)
    {
        out << msg._name;
    }
};

class ThreadFormatItem : public FormatItem
{
public:
    virtual void format(ostream &out, const LogMsg &msg)
    {
        out << msg._tid;
    }
};

class TimeFormatItem : public FormatItem
{
private:
    string _format;

public:
    TimeFormatItem(const string &fmt = "%H:%M:%S") : _format(fmt) {}
    virtual void format(ostream &out, const LogMsg &msg)
    {
        struct tm t;
        localtime_r(&msg._ctime, &t);
        char tmp[32] = {0};
        strftime(tmp, 31, _format.c_str(), &t);
        out << tmp;
    }
};

class CFileFormatItem : public FormatItem
{
public:
    virtual void format(ostream &out, const LogMsg &msg)
    {
        out << msg._file;
    }
};

class CLineFormatItem : public FormatItem
{
public:
    virtual void format(ostream &out, const LogMsg &msg)
    {
        out << msg._line;
    }
};

class TabFormatItem : public FormatItem
{
public:
    virtual void format(ostream &out, const LogMsg &msg)
    {
        out << "\t";
    }
};
class NLineFormatItem : public FormatItem
{
public:
    virtual void format(ostream &out, const LogMsg &msg)
    {
        out << "\n";
    }
};

class OtherFormatItem : public FormatItem
{
private:
    string _str;
public:
    OtherFormatItem(const string &str) : _str(str) {}
    virtual void format(ostream &out, const LogMsg &msg)
    {
        out << _str;
    }
};

class Formatter
{
private:
    string _pattern;
    vector<FormatItem::ptr> _items;

public:
    using ptr = shared_ptr<Formatter>;
    Formatter(const string &pattern = "[%d{%H:%M:%S}][%t][%p][%c][%f:%l] %m%n") : _pattern(pattern)
    {
        assert(parsePattern());
    }
    string format(const LogMsg &msg)
    {
        stringstream ss;
        format(ss, msg);
        return ss.str();
    }
    ostream &format(ostream &os, const LogMsg &msg)
    {
        for (auto &it : _items)
        {
            it->format(os, msg);
        }
        return os;
    }
    FormatItem::ptr createItem(const string &key, const string &val)
    {
        if (key == "d")
            return make_shared<TimeFormatItem>(val);
        if (key == "t")
            return make_shared<ThreadFormatItem>();
        if (key == "c")
            return make_shared<NameFormatItem>();
        if (key == "n")
            return make_shared<NLineFormatItem>();
        if (key == "f")
            return make_shared<CFileFormatItem>();
        if (key == "m")
            return make_shared<MsgFormatItem>();
        if (key == "p")
            return make_shared<LevelFormatItem>();
        if (key == "l")
            return make_shared<CLineFormatItem>();
        if (key == "T")
            return make_shared<TabFormatItem>();

        return make_shared<OtherFormatItem>(val);
    }

private:
    bool parsePattern()
    {
        //[%d{%H:%M:%S}][%t][%p][%c][%f:%l] %m%n
        vector<pair<string, string>> fmt_order;
        size_t pos=0;
        string key, val;
        while (pos < _pattern.size())
        {
            if (_pattern[pos] != '%')
            {
                val.push_back(_pattern[pos++]);
                continue;
            }
            if (pos + 1 < _pattern.size() && _pattern[pos + 1] == '%')
            {
                val.push_back('%');
                pos += 2;
                continue;
            }
        //[%d{%H:%M:%S}][%t][%p][%c][%f:%l] %m%n
            if (val.empty() == false)
            {
                fmt_order.push_back(make_pair("", val));
                val.clear();
            }

            pos += 1;
            if (pos == _pattern.size())
            {
                cout << "%之后，没有对应的格式化字符串！\n";
                return false;
            }

            key += _pattern[pos];
            pos += 1;
            if (pos < _pattern.size() && _pattern[pos] == '{')
            {
                pos+=1;
                while (pos < _pattern.size() && _pattern[pos] != '}')
                    val.push_back(_pattern[pos++]);

                if (pos == _pattern.size())
                {
                    cout << "子规则{}匹配出错";
                    return false;
                }
                pos += 1;
            }
            fmt_order.push_back(make_pair(key, val));
            key.clear();
            val.clear();
        }
        for (auto &it : fmt_order)
        {
            _items.push_back(createItem(it.first, it.second));
        }
        return true;
    }
};
#endif